//#if !UNITY_EDITOR
//#define CURL_DOWNLOAD
//#endif

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using System.Net;
using System.IO;
using System.Threading;
using UnityEngine;
using Frankfort.Threading.Internal;
using Newtonsoft.Json;
using AOT;

public enum DownloadStep
{
	ReadyConnect = 0,
	Connecting,
	Connected,
	Downloading,
	Downloaded,
}

public enum DownloadError
{
	None = 0,
	Unknown,
	ConnectError,
	ConnectError2,
	DownloadError,
	WriteOuputFileError,
	DownloadEncodingError,
	DownloadSizeError,
	FileOperationFailed,
	DecompressFailed,
    CheckHashFailed,
}

public abstract class HttpRedirectHelper
{
	public abstract string ChangeUrl(string url, int hostIndex);
	public abstract int Count();
}

public class EncodingException : System.Exception
{
	public EncodingException(string message)
		:base(message)
	{
		
	}
}

public class SizeException : System.Exception
{
	public SizeException(string message)
		:base(message)
	{
		
	}
}

class HttpDownloadRequest
{
	public delegate void HttpDownloadCallback(HttpDownloadRequest request, string url, string localPath, DownloadError status);
	public delegate bool SuccessThreadCallBack();
	
	public const bool isSupportContinue = false;
	
	private uint _offset;
	private uint _size;
	private uint _decompressSize;
	private uint _totalSize;
	private uint _compressType;
	public string _originalUrl;
	private int urlIndex = -1;
	private HttpRedirectHelper _redirectHelper;
	public string _localPath;
	private SuccessThreadCallBack _threadCallback;
	private HttpDownloadCallback _callback;
	#if CURL_DOWNLOAD
	private EvonyHttpDownloadHelper2 _request;
	#else
	private HttpDownloadHelperEx _request;
	#endif
	private int readedSum;
	private bool _isCancel = false;
	private int _maxRetryCount;
	private int curRetryCount;
	private int badRetryCount;
	private int sizeErrorCount;
	private int totalConnectNum;
	private float startTime;
	
	//for speed
	private int tempReadedSum;
	private float tempStartTime;
	private float speed;
	private DownloadStep step;
	private bool _createThread;
	
	private static int _seq = 0;
	private string _cur_seq = "";
	
	public int progress
	{
		get
		{
			if(_request != null)
			{
				return _request.progress;
			}
			return readedSum;
		}
	}
	
	public HttpDownloadRequest(string url, string localPath, uint offset, uint size, uint decompressSize, uint totalSize, uint compressType, int maxRetryCount, HttpRedirectHelper redirectHelper, HttpDownloadCallback callback, SuccessThreadCallBack callBack2)
	{
		this._originalUrl = url;
		this._localPath = localPath;
		this._offset = offset;
		this._size = size;
		this._decompressSize = decompressSize;
		this._totalSize = totalSize;
		this._compressType = compressType;
		this._maxRetryCount = maxRetryCount;
		this._redirectHelper = redirectHelper;
		this._callback = callback;
		this._threadCallback = callBack2;
	}
	
	public float getSpeed()
	{
		return speed;
	}
	
	public int getTotalConnectCount()
	{
		return totalConnectNum;
	}
	
	public int getBadRetryCount()
	{
		return badRetryCount;
	}
	
	private void addProgress(int sum)
	{
		readedSum += sum;
	}
	
	public float getStartTime()
	{
		return startTime;
	}
	
	public bool isConnecting()
	{
		return step == DownloadStep.Connecting;
	}
	
	public bool isReadyConnect()
	{
		return step == DownloadStep.ReadyConnect;
	}
	
	private void realRequest(bool createThread = true)
	{
		string url = null;
		if(_redirectHelper != null)
		{
			if(urlIndex >= 0)
			{
				url = _redirectHelper.ChangeUrl(_originalUrl, urlIndex);
				if(url == null)
				{
					urlIndex = -1;
				}
			}
			
			if(urlIndex < 0)
			{
				url = _originalUrl;
			}
			
			urlIndex++;
		}
		else
		{
			url = _originalUrl;
		}
		
		this.readedSum = 0;
		
		if(isSupportContinue)
		{
			int start = 0;
			if(File.Exists(_localPath))
			{
				FileStream fileStream = File.OpenRead(_localPath);
				start = (int)fileStream.Length;
				fileStream.Close();
			}
			
			if(_size != 0 && start >= _size)
			{
				if(start > _size)
				{
					CommonUtil.Delete(_localPath);
					start = 0;
				}
				else
				{
					readedSum = start;
					this.RepsonseSuccess();
					return;
				}
			}
			
			if(start > 0)
			{
				readedSum = start;
			}
		}
		
		totalConnectNum++;
		
		
//			#if CURL_DOWNLOAD
//			_request = new EvonyHttpDownloadHelper2();
//			_request.download(createThread, url, this._localPath, _originalUrl, _offset, _size, _decompressSize, _compressType, readedSum, _totalSize, setStep, RepsonseSuccess, ResponseError);
//			#else
			_request = new HttpDownloadHelperEx();
			_request.download(createThread, url, this._localPath, _originalUrl, _offset, _size, readedSum, _totalSize, setStep, RepsonseSuccess, ResponseError);
//			#endif

	}
	
	
	private void setStep(DownloadStep step)
	{
		this.step = step;
	}
	
	public void Request(bool createThread = true)
	{
		this._createThread = createThread;
		
		this._isCancel = false;
		if(this.startTime == 0)
		{
			//			this.startTime = Time.time;
		}
		
		step = DownloadStep.ReadyConnect;
		
		realRequest(createThread);
	}
	
	public void Cancel(bool isSuccess = false)
	{
		lock(this)
		{
			_isCancel = true;
			
			if (_request != null)
			{
				_request.Abort();
				//				_request = null;
			}
			
			_callback = null;
		}
	}
	
	private void ResponseError(DownloadError status)
	{
		curRetryCount++;

		if(status == DownloadError.FileOperationFailed)
		{
			this._maxRetryCount = 0;
		}

		if(!canRetry)
		{
			mainThreadResponse(status);
			return;
		}
		
		if(_createThread)
		{
			mainThreadRetry(status);
		}
	}
	
	private void RepsonseSuccess()
	{
		this.setStep(DownloadStep.Downloaded);
		
		if(_threadCallback != null)
		{
			_threadCallback();
		}
		
		mainThreadResponse(DownloadError.None);
	}
	
	private void mainThreadResponse(DownloadError status)
	{
		if(!Loom.CheckIfMainThread())
		{
			MainThreadDispatcher.DispatchToMainThread((object obj) => 
			                                          {
				mainThreadResponse(status);
			}, null);
			return;
		}
		
		if (_callback != null)
		{
			HttpDownloadCallback temp = _callback;
			_callback = null;
			temp(this, _originalUrl, _localPath, status);
		}
	}
	
	private void mainThreadRetry(DownloadError status)
	{
		if(!Loom.CheckIfMainThread())
		{
			MainThreadDispatcher.DispatchToMainThread((object obj) => 
			                                          {
				mainThreadRetry(status);
			}, null);
			return;
		}
		
		realRequest();
	}
	
	private void mainThreadAddTempSum(object status)
	{
		int sum = (int)status;
		addProgress(sum);
	}
	
//	private void OnHttpDownloadStatusChanged(DataChangeType type, params object[] objs)
//	{
//		if (_isCancel) 
//		{
//			return;
//		}
//		
//		if (objs == null || objs.Length == 0) 
//		{
//			return;
//		}
//		
//		if (_cur_seq == null || _cur_seq.Length == 0) 
//		{
//			return;
//		}
//		
//		Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>> ((string)objs[0]);
//		if (dic.ContainsKey ("seq") == false) 
//		{
//			return;
//		}
//		
//		string seq = dic["seq"];
//		if (_cur_seq.Equals (seq) == false) 
//		{
//			return;
//		}
//		
//		this.step = (DownloadStep)int.Parse (dic["status"]);
//		DownloadError error = (DownloadError)int.Parse (dic["error"]);
//		if (step == DownloadStep.Downloaded) 
//		{
//			this.mainThreadResponse(DownloadError.None);
//		}
//		else 
//		{
//			return ;
//			if (error != DownloadError.None)
//			{
//				this.curRetryCount++;
//				this.badRetryCount++;
//				
//				if (error == DownloadError.DownloadSizeError)
//				{
//					sizeErrorCount++;
//				}
//				
//				if (this.curRetryCount < this._maxRetryCount + 1)
//				{
//					this.realRequest();
//				}
//				else
//				{
//					this.mainThreadResponse(error);
//				}
//			}
//		}
//	}
//	
//	private void OnHttpDownloadProgressChanged(DataChangeType type, params object[] objs)
//	{
//		if (_isCancel) 
//		{
//			return;
//		}
//		
//		if (objs == null || objs.Length == 0) 
//		{
//			return;
//		}
//		
//		if (_cur_seq == null || _cur_seq.Length == 0) 
//		{
//			return;
//		}
//		
//		Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>> ((string)objs[0]);
//		if (dic.ContainsKey ("seq") == false || dic.ContainsKey("downloadSize") == false) 
//		{
//			return;
//		}
//		
//		string seq = dic["seq"];
//		if (_cur_seq.Equals (seq) == false) 
//		{
//			return;
//		}
//		
//		int downloadSize = int.Parse(dic["downloadSize"]);
//		this.mainThreadAddTempSum (downloadSize);
//	}
	
	public bool completed
	{
		get
		{
			return this.step == DownloadStep.Downloaded;
		}
	}
	
	public bool canRetry
	{
		get
		{
			return !(curRetryCount >= this._maxRetryCount + 1 || _isCancel);
		}
	}
}
